/**
 *
 * Copyright 2009 Marco Speranza <marco.speranza79@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.commons.database.jconnectionpool;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.ConnectionPoolDataSource;

import org.apache.log4j.Logger;

/**
 * Classe che crea e gestisce le richieste di connessione al DB
 * 
 * Un tipo uso e':
 * 
 * <pre>
 * 	DataBaseManager dbManager = new DataBaseManager(); 	
 * dbManager.initMySqlConnectionPoolDataSource(&quot;jdbc:mysql://localhost:3306/mysql&quot;, &quot;user&quot;, &quot;pass&quot;); *		
 * dbManager.setInitialPoolCapacity(100);
 * dbManager.open();	
 * Connection c = dbManager.getConnection();
 * 
 * ...doSomething(c);...	
 * 
 * dbManager.releaseConnection(c);
 * dbManager.close();
 * 
 * </pre>
 * 
 * 
 * 
 * @author <a href="mailto:msperanza@users.sourceforge.net">Marco Speranza</a>
 * @version $Id: DataBaseManager.java,v 1.1 2006/08/10 09:48:09 msperanza Exp $
 */
public class DataBaseManager implements IDataBaseManager {

    private static Logger log = Logger.getLogger(DataBaseManager.class);
    private PoolConnectionManager poolConnManager;

    private String connectionString;
    private String user;
    private String password;
    private int initialCapacity;

    /**
     * Costruttore di default
     * 
     */
    public DataBaseManager() {
	this(null, null, null);
    }

    /**
     * Costruttore
     * 
     * @param connectionString
     *            jdbc connection string
     * @throws SQLException
     *             in caso di errore
     */
    public DataBaseManager(String connectionString) {
	this(connectionString, null, null);
    }

    /**
     * Costruttore
     * 
     * @param connectionString
     *            jdbc connection string
     * @param user
     *            db user
     * @param password
     *            db password
     * @throws SQLException
     *             in caso di errore
     */
    public DataBaseManager(String connectionString, String user, String password) {
	this.connectionString = connectionString;
	this.user = user;
	this.password = password;
    }

    /**
     * Chiude la connessione con il DB
     * 
     * @throws SQLException
     *             Errore nella chiusura della connessione al DB
     */
    public void close() throws SQLException {
	log.info("Close connection pool");
	poolConnManager.closePool();
    }

    /**
     * Torna una connessione nuova, oppure una disponibile al DB
     * 
     * @return <code> java.sql.Connection</code> la connessiona al DB
     * @throws SQLException
     *             in caso di errore
     */
    public Connection getConnection() throws SQLException {
	Connection con = poolConnManager.getConnection();
	return con;
    }

    /**
     * Verifica se il database è attivo.
     * 
     * @return
     */
    public boolean isDatabaseOn() {
	if (poolConnManager == null) {
	    throw new NullPointerException(
		    "Il pool di connessione deve essere inizializzato. Invocare il metodo init().");
	}
	return poolConnManager.isDatabaseOn();
    }

    /**
     * Inizializza il pool di connessione
     * 
     * @param connectionPoolDataSourceClassName
     *            class name da instanziare
     * @param connectionString
     *            jdbc url
     * @param username
     *            database username
     * @param password
     *            database password
     * @param initialCapacity
     *            capacita' iniziale del pool di connessione
     * @throws ClassNotFoundException
     *             lanciata se non esiste la classe
     *             <code>connectionPoolDataSourceClassName</code>
     * @throws Exception
     *             In caso di errore interno
     */
    public void init(ConnectionPoolDataSource connectionPoolDataSource) {
	poolConnManager = new PoolConnectionManager(connectionPoolDataSource);
    }

    /**
     * Inizializza il pool di connessione con una capacita' iniziale di default
     * (60).
     * 
     * @param connectionPoolDataSourceClassName
     *            class name da instanziare
     * @throws ClassNotFoundException
     *             lanciata se non esiste la classe
     *             <code>connectionPoolDataSourceClassName</code>
     * @throws Exception
     *             In caso di errore interno
     * 
     */
    public void init(String connectionPoolDataSourceClassName)
	    throws ClassNotFoundException, Exception {
	init(connectionPoolDataSourceClassName, getConnectionString(),
		getUser(), getPassword(), 60);
    }

    /**
     * Inizializza il pool di connessione
     * 
     * @param connectionPoolDataSourceClassName
     *            class name da instanziare
     * @throws ClassNotFoundException
     *             lanciata se non esiste la classe
     *             <code>connectionPoolDataSourceClassName</code>
     * @throws Exception
     *             In caso di errore interno
     * 
     */
    public void init(String connectionPoolDataSourceClassName,
	    int initialCapacity) throws ClassNotFoundException, Exception {
	init(connectionPoolDataSourceClassName, getConnectionString(),
		getUser(), getPassword(), initialCapacity);
    }

    /**
     * Inizializza il pool di connessione con una capacita' iniziale di default
     * (60).
     * 
     * @param connectionPoolDataSourceClassName
     *            class name da instanziare
     * @param connectionString
     *            jdbc url
     * @param username
     *            database username
     * @param password
     *            database password
     * @throws ClassNotFoundException
     *             lanciata se non esiste la classe
     *             <code>connectionPoolDataSourceClassName</code>
     * @throws Exception
     *             In caso di errore interno
     * 
     */
    public void init(String connectionPoolDataSourceClassName,
	    String connectionString, String username, String password)
	    throws ClassNotFoundException, Exception {
	init(connectionPoolDataSourceClassName, connectionString, username,
		password, 60);
    }

    /**
     * Inizializza il pool di connessione
     * 
     * @param connectionPoolDataSourceClassName
     *            class name da instanziare
     * @param connectionString
     *            jdbc url
     * @param username
     *            database username
     * @param password
     *            database password
     * @param initialCapacity
     *            capacita' iniziale del pool di connessione
     * @throws ClassNotFoundException
     *             lanciata se non esiste la classe
     *             <code>connectionPoolDataSourceClassName</code>
     * @throws Exception
     *             In caso di errore interno
     */
    public void init(String connectionPoolDataSourceClassName,
	    String connectionString, String username, String password,
	    int initialCapacity) throws ClassNotFoundException, Exception {
	setConnectionString(connectionString);
	setPassword(password);
	setUser(username);

	DefaultConnectionPoolDataSource defaultConnectionPoolDataSource = new DefaultConnectionPoolDataSource(
		connectionPoolDataSourceClassName);
	defaultConnectionPoolDataSource.init(getConnectionString(), getUser(),
		getPassword());
	ConnectionPoolDataSource connectionPoolDataSource = defaultConnectionPoolDataSource
		.getConnectionPoolDataSource();

	poolConnManager = new PoolConnectionManager(connectionPoolDataSource);
	setInitialPoolCapacity(initialCapacity);

    }

    /**
     * Inizializza il pool di connessione di default per un database Mysql
     * 
     * @param connectionString
     *            jdbc url
     * @param username
     *            database username
     * @param password
     *            database password
     * @throws ClassNotFoundException
     *             lanciata se non esiste la classe
     *             <code>DefaultConnectionPoolDataSource.MYSQL_POOL_CLASS_NAME</code>
     * @throws Exception
     *             In caso di errore interno
     */
    public void initMySqlConnectionPoolDataSource(String connectionString,
	    String username, String password) throws ClassNotFoundException,
	    Exception {
	init(DefaultConnectionPoolDataSource.MYSQL_POOL_CLASS_NAME,
		connectionString, username, password, 60);
    }

    /**
     * Inizializza il pool di connessione di default per un database Oracle
     * 
     * @param connectionString
     *            jdbc url
     * @param username
     *            database username
     * @param password
     *            database password
     * @throws ClassNotFoundException
     *             lanciata se non esiste la classe
     *             <code>DefaultConnectionPoolDataSource.ORACLE_POOL_CLASS_NAME</code>
     * @throws Exception
     *             In caso di errore interno
     */
    public void initOracleConnectionPoolDataSource(String connectionString,
	    String username, String password) throws ClassNotFoundException,
	    Exception {
	init(DefaultConnectionPoolDataSource.ORACLE_POOL_CLASS_NAME,
		connectionString, username, password, 60);
    }

    /**
     * apre una sessione sul DB utilizzando i dati di classe
     * 
     * @throws SQLException
     *             Errore di connessione al DB
     */
    public void open() throws SQLException {
	poolConnManager.setInitialPoolCapacity(initialCapacity);
	poolConnManager.createPool();
	log.info("Connect to db done url:" + connectionString + " user:" + user
		+ " pwd:" + password);
    }

    /**
     * Rilascia una connessione al DB
     * 
     * @param conn
     *            Connessione da rilasciare
     * @throws SQLException
     *             Errore nella chiusura della connessione al DB
     */
    public void releaseConnection(Connection conn) throws SQLException {
	poolConnManager.closeConnection(conn);
    }

    /**
     * setta la connection string al db
     * 
     * @param connectionString
     *            la jdbc connection string
     */
    public void setConnectionString(String connectionString) {
	this.connectionString = connectionString;
    }

    /**
     * Setta la capacita' iniziale del pool di connessione.
     * 
     * @param initialCapacity
     *            la capacita' iniziale.
     */
    public void setInitialPoolCapacity(int initialCapacity) {
	this.initialCapacity = initialCapacity;
    }

    /**
     * setta la password del database
     * 
     * @param password
     *            la password del db
     */
    public void setPassword(String password) {
	this.password = password;
    }

    /**
     * setta l'utente del database
     * 
     * @param password
     *            l'utente del db
     */
    public void setUser(String user) {
	this.user = user;
    }

    /**
     * torna la stringa di connessione jdbc
     * 
     * @return String la string di connessional db
     */
    private String getConnectionString() {
	return connectionString;
    }

    private String getPassword() {
	return password;
    }

    int getPoolSize() {
	return poolConnManager.getPoolSize();
    }

    private String getUser() {
	return user;
    }

}
